unit QExport4ODT;

{$I QExport4VerCtrl.inc}

interface

uses Classes, QExport4, QExport4IniFiles, QExport4BaseODFClass
  {$IFDEF WIN32}
    {$IFNDEF NOGUI}, Graphics{$ELSE}, QExport4Graphics{$ENDIF}
  {$ENDIF}
  {$IFDEF LINUX}
    {$IFNDEF NOGUI}, QGraphics{$ELSE}, QExport4Graphics{$ENDIF}
  {$ENDIF};

type
  TQExportODTOptions = class(TPersistent)
  private
    FHolder: TPersistent;
    FHeaderStyle: TODTParagraphStyle;
    FFooterStyle: TODTParagraphStyle;
    FCaptionRowStyle: TODTCellParagraphStyle;
    FDataStyle: TODTCellParagraphStyle;
    FStripStyle: TODFStripStyleType;
    FStripStylesList: TODTStylesList;
    FFontList: TStrings;
    FBorder: TODFBorder;

    procedure SetHeaderStyle(const Value: TODTParagraphStyle);
    procedure SetFooterStyle(const Value: TODTParagraphStyle);
    procedure SetCaptionStyle(const Value: TODTCellParagraphStyle);
    procedure SetDataStyle(const Value: TODTCellParagraphStyle);
    procedure SetStripStyles(const Value: TODTStylesList);
  protected
    function GetOwner: TPersistent; override;
  public
    constructor Create(Holder: TPersistent);
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    function CheckFontInList(FontName: string): Boolean;

    property FontList: TStrings read FFontList write FFontList;
  published
    property HeaderStyle: TODTParagraphStyle read FHeaderStyle
      write SetHeaderStyle;
    property FooterStyle: TODTParagraphStyle read FFooterStyle
      write SetFooterStyle;
    property CaptionRowStyle: TODTCellParagraphStyle read FCaptionRowStyle
      write SetCaptionStyle;
    property DataStyle: TODTCellParagraphStyle read FDataStyle write SetDataStyle;
    property StripStyle: TODFStripStyleType read FStripStyle write FStripStyle
      default sstNone;
    property StripStylesList: TODTStylesList read FStripStylesList
      write SetStripStyles;
    property Border: TODFBorder read FBorder write FBorder;
  end;

  TQExport4ODT = class(TQExport4AdvancedText)
  private
    FODTFile: TBaseODFFile;
    FTableName: String;
    FODTOptions: TQExportODTOptions;
    FRowCounter: Integer;
    FBufferRow: array of WideString;
    function ColorToString(Color: TColor): string;
    function PointsToCms(Value: Integer): string;
    procedure SetOptions(const Value: TQExportODTOptions);
  protected
    procedure BeginExport; override;
    procedure EndExport; override;
    procedure AfterExport; override;

    procedure WriteDataRow; override;
    procedure WriteCaptionRow; override;
    procedure WriteBufferedRow(TypeOfRow: Integer);
    procedure ClearBufferedRow;
    procedure AddStyles;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;

    procedure Execute; override;
    property ODTFile: TBaseODFFile read FODTFile write FODTFile;
  published
    property AllowCaptions;
    property Captions;
    property UserFormats;
    property TableName: String read FTableName write FTableName;
    property ODTOptions: TQExportODTOptions read FODTOptions write SetOptions;
  end;

implementation

uses {$IFDEF WIN32}QExport4StrIDs{$ENDIF}
     {$IFDEF LINUX}QExport4Consts{$ENDIF},
     SysUtils, QExport4Common, QExport4EmsWideStrUtils;


{ TQExport4ODT }

procedure TQExport4ODT.AddStyles;
var
  I: Integer;

  procedure WriteFontStyleNode(TypeOfOptions: Integer);

    procedure SubRoutine(TempPar: TODTParagraphStyle);
    begin
      ODTFile.WriteSpecificNode(20, '', ['style:name', 'svg:font-family',
        'style:font-pitch'], [TempPar.Font.Name, TempPar.Font.Name, 'variable']);
      ODTOptions.FontList.Add(TempPar.Font.Name);
    end;

  begin
    if (TypeOfOptions = 0) then
      if not (ODTOptions.CheckFontInList('Arial')) then
      begin
        ODTFile.WriteSpecificNode(20, '', ['style:name', 'svg:font-family',
          'style:font-pitch'], ['Arial', 'Arial', 'variable']);
        ODTOptions.FontList.Add('Arial');
      end;
    if TypeOfOptions = 1 then
      if not (ODTOptions.CheckFontInList(ODTOptions.HeaderStyle.Font.Name)) then
        SubRoutine(ODTOptions.HeaderStyle);
    if TypeOfOptions = 2 then
      if not (ODTOptions.CheckFontInList(ODTOptions.FooterStyle.Font.Name)) then
        SubRoutine(ODTOptions.FooterStyle);
    if TypeOfOptions = 3 then
      SubRoutine(ODTOptions.CaptionRowStyle);
    if TypeOfOptions = 4 then
      if not (ODTOptions.CheckFontInList(ODTOptions.DataStyle.Font.Name)) then
        SubRoutine(ODTOptions.DataStyle);
    if TypeOfOptions > 4 then
      if not (ODTOptions.CheckFontInList(ODTOptions.StripStylesList[TypeOfOptions - 5].Font.Name)) then
        SubRoutine(ODTOptions.StripStylesList[TypeOfOptions - 5]);
  end;

  procedure WriteCellStyleNode(TypeOfOptions: Integer; Prefix: WideString);

    procedure SubRoutine(TempPar: TODTCellParagraphStyle; Name: WideString);
    var
      AttList, AttValues: array of WideString;
      Num: integer;
    begin
      //Common style declaring section
      ODTFile.WriteSpecificNode(21, '', ['style:name','style:family'],
        [Name, 'table-cell']);
      SetLength(AttList, 6);
      SetLength(AttValues, 6);
      AttList[0] := 'fo:padding';
      AttValues[0] := '0.035cm';
      if (ODTOptions.Border.BorderStyle = bsODFSolid) then
      begin
        AttList[1] := 'fo:border-top';
        AttValues[1] := PointsToCms(ODTOptions.Border.BorderWidth) +
          ' ' + 'solid' + ' ' + ColorToString(ODTOptions.Border.BorderColor);
        AttList[2] := 'fo:border-left';
        AttValues[2] := PointsToCms(ODTOptions.Border.BorderWidth) +
          ' ' + 'solid' + ' ' + ColorToString(ODTOptions.Border.BorderColor);
        if QEPos('Layer', Name) <> 0 then
        begin
          AttList[4] := 'fo:border-bottom';
          AttValues[4] := PointsToCms(ODTOptions.Border.BorderWidth) +
                            ' ' + 'solid' + ' ' +
                              ColorToString(ODTOptions.Border.BorderColor);
        end
        else
        begin
          AttList[4] := 'fo:border-bottom';
          AttValues[4] := 'none';
        end;
        if QEPos('Ender', Name) <> 0 then
        begin
          AttList[5] := 'fo:border-right';
          AttValues[5] := PointsToCms(ODTOptions.Border.BorderWidth) +
                            ' ' + 'solid' + ' ' +
                              ColorToString(ODTOptions.Border.BorderColor);
        end
        else
        begin
          AttList[5] := 'fo:border-right';
          AttValues[5] := 'none';
        end;
      end
      else
      begin
        AttList[1] := 'fo:border-top';
        AttValues[1] := 'none';
        AttList[2] := 'fo:border-left';
        AttValues[2] := 'none';
        AttList[4] := 'fo:border-bottom';
        AttValues[4] := 'none';
        AttList[5] := 'fo:border-right';
        AttValues[5] := 'none';
      end;
      case TempPar.VerticalAligment of
        taODFTop: begin
                    AttList[3] := 'style:vertical-align';
                    AttValues[3] := 'top';
                  end;
        taODFMiddle: begin
                       AttList[3] := 'style:vertical-align';
                       AttValues[3] := 'middle';
                     end;
        taODFBottom: begin
                       AttList[3] := 'style:vertical-align';
                       AttValues[3] := 'bottom';
                     end;
      end;
      if (TempPar.AllowBackground) then
      begin
        Num := Length(AttValues);
        SetLength(AttList, Num + 1);
        SetLength(AttValues, Num + 1);
        AttList[Num] := 'fo:background-color';
        AttValues[Num] := ColorToString(TempPar.BackgroundColor);
      end;
      ODTFile.WriteSpecificNode(25, '', AttList, AttValues);
      ODTFile.WriteSpecificNode(1021, '', [], []);
    end;
  begin
    //for CaptionRow
    if (TypeOfOptions = 1) then
      SubRoutine(ODTOptions.CaptionRowStyle, Prefix + 'CaptionCellRow');
    //For DataRow
    if (TypeOfOptions = 2) then
      SubRoutine(ODTOptions.DataStyle, Prefix + 'DataCellStyle');
    //for stripes styles
    if (TypeOfOptions > 2) then
      SubRoutine(ODTOptions.StripStylesList[TypeOfOptions - 3],
        Prefix + 'StripCellStyle' + IntToStr(TypeOfOptions - 2));
  end;

  procedure WriteParagraphStyleNode(TypeOfOptions: Integer);

    procedure SubRoutine(TempPar: TODTParagraphStyle; Name: WideString);
    var
      AttList, AttValues: array of WideString;
      Num: integer;
    begin
      SetLength(AttList, 3);
      SetLength(AttValues, 3);
      AttList[0] := 'style:family';
      AttValues[0] := 'paragraph';
      AttList[1] := 'style:parent-style-name';
      AttValues[1] := 'Standard';
      AttList[2] := 'style:name';
      AttValues[2] := Name;
      ODTFile.WriteSpecificNode(21, '', AttList, AttValues);
      AttList[0] := 'style:font-name';
      AttValues[0] := TempPar.Font.Name;
      AttList[1] := 'fo:font-size';
      AttValues[1] := IntToStr(TempPar.Font.Size) + 'pt';
      AttList[2] := 'fo:color';
      AttValues[2] := ColorToString(TempPar.Font.Color);
      if (fsItalic in TempPar.Font.Style) then
      begin
        Num := Length(AttValues);
        SetLength(AttList, Num + 1);
        SetLength(AttValues, Num + 1);
        AttList[Num] := 'fo:font-style';
        AttValues[Num] := 'italic';
      end;
      if (fsBold in TempPar.Font.Style) then
      begin
        Num := Length(AttValues);
        SetLength(AttList, Num + 1);
        SetLength(AttValues, Num + 1);
        AttList[Num] := 'fo:font-weight';
        AttValues[Num] := 'bold';
      end;
      if (fsUnderline in TempPar.Font.Style) then
      begin
        Num := Length(AttValues);
        SetLength(AttList, Num + 3);
        SetLength(AttValues, Num + 3);
        AttList[Num] := 'style:text-underline-style';
        AttValues[Num] := 'solid';
        AttList[Num + 1] := 'text-underline-width';
        AttValues[Num + 1] := 'auto';
        AttList[Num + 2] :='style:text-underline-color';
        AttValues[Num + 2] := 'font-color';
      end;
      if (fsStrikeOut in TempPar.Font.Style) then
      begin
        Num := Length(AttValues);
        SetLength(AttList, Num + 1);
        SetLength(AttValues, Num + 1);
        AttList[Num] := 'style:text-line-through-style';
        AttValues[Num] := 'solid';
      end;
      if (TempPar.AllowHighlight) then
      begin
        Num := Length(AttValues);
        SetLength(AttList, Num + 1);
        SetLength(AttValues, Num + 1);
        AttList[Num] := 'fo:background-color';
        AttValues[Num] := ColorToString(TempPar.HighlightColor);
      end;
      ODTFile.WriteSpecificNode(26, '', AttList, AttValues);
      SetLength(AttList, 1);
      SetLength(AttValues, 1);
      case TempPar.Alignment of
        taODFLeft:
          begin
            AttList[0] := 'fo:text-align';
            AttValues[0] := 'start';
          end;
        taODFRight:
          begin
            AttList[0] := 'fo:text-align';
            AttValues[0] := 'end';
          end;
        taODFCenter:
          begin
            AttList[0] := 'fo:text-align';
            AttValues[0] := 'center';
          end;
        taODFJustify:
          begin
            AttList[0] := 'fo:text-align';
            AttValues[0] := 'justify';
          end;
      end;
      if (TempPar.AllowBackground) then
      begin
        SetLength(AttList, 2);
        SetLength(AttValues, 2);
        AttList[1] := 'fo:background-color';
        AttValues[1] := ColorToString(TempPar.BackgroundColor);
      end;
      ODTFile.WriteSpecificNode(29, '', AttList, AttValues);
      ODTFile.WriteSpecificNode(1021, '', [], []);
    end;
  begin
    if (TypeOfOptions = 1) then
      SubRoutine(ODTOptions.HeaderStyle, 'Header');
    if (TypeOfOptions = 2) then
      SubRoutine(ODTOptions.FooterStyle, 'Footer');
    if (TypeOfOptions = 3) then
      SubRoutine(ODTOptions.CaptionRowStyle, 'CaptionRow');
    if (TypeOfOptions = 4) then
      SubRoutine(ODTOptions.DataStyle, 'DataStyle');
    if (TypeOfOptions > 4) then
      SubRoutine(ODTOptions.StripStylesList[TypeOfOptions - 5],
        'StripStyle' + IntToStr(TypeOfOptions - 4));
  end;

  procedure AddLayersAndEnderBorderStyles;
  var
    Condition: Boolean;
    Num, CheckNum, I: Integer;
  begin
    //Check of data existence in formed table
    Condition := ((FRowCounter > 1)
      and (AllowCaptions)) or ((FRowCounter >= 1) and not(AllowCaptions));
    //Only caption row used - we should define border styles
    //only for caption row
    if (FRowCounter = 1) and (AllowCaptions) then
    begin
      WriteCellStyleNode(1, 'Layer');
      WriteCellStyleNode(1, 'LayerEnder');
    end;
    //At least one row in table exists - need to form data styles
    if (ODTOptions.StripStyle = sstNone) and Condition then
    begin
      if (AllowCaptions) then
        WriteCellStyleNode(1, 'Ender');      
      WriteCellStyleNode(2, 'Layer');
      WriteCellStyleNode(2, 'LayerEnder');
      WriteCellStyleNode(2, 'Ender');
    end;
    //At least one row in table exists and row style defined
    //need to make all ender/layer borders for strip styles
    if (ODTOptions.StripStyle = sstRow) and Condition then
    begin
      if (AllowCaptions) then
      begin
        Num := (FRowCounter - 1) mod ODTOptions.StripStylesList.Count;
        CheckNum := (FRowCounter - 1) div ODTOptions.StripStylesList.Count;
      end
      else
      begin
        Num := FRowCounter mod ODTOptions.StripStylesList.Count;
        CheckNum := FRowCounter div ODTOptions.StripStylesList.Count;
      end;
      if Num = 0 then
        Num := ODTOptions.StripStylesList.Count;
      //if Row Counter greater than number of strip styles
      //then form all ender strip styles
      //in other cases - form till num achived
      if CheckNum > 0 then
        for I := 0 to ODTOptions.StripStylesList.Count - 1 do
          WriteCellStyleNode(I + 3, 'Ender')
      else
        for I := 0 to Num - 2 do
          WriteCellStyleNode(I + 3, 'Ender');
      //The last strip style must have layer
      WriteCellStyleNode(Num + 2, 'Layer');
      WriteCellStyleNode(Num + 2, 'LayerEnder');
      if (AllowCaptions) then
        WriteCellStyleNode(1, 'Ender');
    end;
    if (ODTOptions.StripStyle = sstColumn) and Condition then
    begin
      Num := Columns.Count mod ODTOptions.StripStylesList.Count;
      if Num = 0 then
        Num := ODTOptions.StripStylesList.Count;
      if (Columns.Count div ODTOptions.StripStylesList.Count) > 0 then
        for I := 0 to ODTOptions.StripStylesList.Count - 1 do
          WriteCellStyleNode(I + 3, 'Layer')
      else
        for I := 0 to Num - 1 do
          WriteCellStyleNode(I + 3, 'Layer');
      //The last strip style must have layer
      WriteCellStyleNode(Num + 2, 'Ender');
      WriteCellStyleNode(Num + 2, 'LayerEnder');
      if (AllowCaptions) then
        WriteCellStyleNode(1, 'Ender');      
    end;
  end;
begin
  //+-------------------------------------+
  //|Default face-fonts processing section|
  //+-------------------------------------+
  ODTFile.OpenStream('facefont.xml');
  ODTFile.OpenWriter;
  for I := 1 to 4 do
  begin
    WriteFontStyleNode(I);
  end;
  for I := 0 to ODTOptions.StripStylesList.Count - 1 do
  begin
    WriteFontStyleNode(I + 5);
  end;
  ODTFile.WriteSpecificNode(1010, '', [], []);
  ODTFile.CloseWriter;
  ODTFile.CloseStream;
  ODTOptions.FontList.Clear;
  ODTFile.OpenStream('facefontstyles.xml');
  ODTFile.OpenWriter;
  for I := 1 to 4 do
    WriteFontStyleNode(I);
  for I := 0 to ODTOptions.StripStylesList.Count - 1 do
    WriteFontStyleNode(I + 5);
  ODTFile.WriteSpecificNode(1010, '', [], []);
  ODTFile.CloseWriter;
  ODTFile.CloseStream;
  //+-----------------------+
  //|Styles creation section|
  //+-----------------------+
  ODTFile.OpenStream('auto.xml');
  ODTFile.OpenWriter;
  ODTFile.WriteSpecificNode(11, '', [], []);
  for I := 1 to 2 do
    WriteCellStyleNode(I, '');
  for I := 0 to ODTOptions.StripStylesList.Count - 1 do
    WriteCellStyleNode(I + 3, '');
  //If Row counter > 0 and we're using border
  //then we have to add border and layer styles section
  if (ODTOptions.Border.BorderStyle = bsODFSolid) and (FRowCounter > 0) then
    AddLayersAndEnderBorderStyles;
  for I := 1 to 4 do
    WriteParagraphStyleNode(I);
  for I := 0 to ODTOptions.StripStylesList.Count - 1 do
    WriteParagraphStyleNode(I + 5);
  ODTFile.WriteSpecificNode(1011, '', [], []);
  ODTFile.WriteSpecificNode(12, '', [], []);
  ODTFile.WriteSpecificNode(14, '', [], []);
  ODTFile.CloseWriter;
  ODTFile.CloseStream;
  ODTFile.OpenStream('autoStyles.xml');
  ODTFile.OpenWriter;
  ODTFile.WriteSpecificNode(27, '', [], []);
  for I := 1 to 2 do
    WriteCellStyleNode(I, '');
  for I := 0 to ODTOptions.StripStylesList.Count - 1 do
    WriteCellStyleNode(I + 3, '');
  //If Row counter > 0 and we're using border
  //then we have to add border and layer styles section
  if (ODTOptions.Border.BorderStyle = bsODFSolid) and (FRowCounter > 0) then
    AddLayersAndEnderBorderStyles;
  for I := 1 to 4 do
    WriteParagraphStyleNode(I);
  for I := 0 to ODTOptions.StripStylesList.Count - 1 do
    WriteParagraphStyleNode(I + 5);
  ODTFile.WriteSpecificNode(1027, '', [], []);
  ODTFile.WriteSpecificNode(211, '', [], []);
  ODTFile.WriteSpecificNode(1028, '', [], []);
  ODTFile.CloseWriter;
  ODTFile.CloseStream;
end;

procedure TQExport4ODT.AfterExport;
begin                       
  if (FRowCounter = 0) then
    WriteBufferedRow(2)
  else
    WriteBufferedRow(3);
end;

procedure TQExport4ODT.BeginExport;
var
  i: Integer;
  temp: WideString;
begin
  inherited;
  ODTFile.Renew;
  FRowCounter := 0;
  SetLength(FBufferRow, Columns.Count);
  ClearBufferedRow;
  ODTFile.TypeConvert := odfText;
  ODTFile.FileName := Self.FileName;
  ODTFile.MakeAll;

  ODTFile.OpenStream('table.xml');
  ODTFile.OpenWriter;
  ODTFile.CheckStatus;
  for i := 0 to Header.Count - 1 do
  begin
    temp := Header[I];
    ODTFile.WriteSpecificNode(18, ReplaceSymbols(Temp), ['text:style-name'], ['Header']);
  end;
  ODTFile.WriteSpecificNode(15, '', ['table:name'], [TableName]);
  for i := 0 to Columns.Count - 1 do
    ODTFile.WriteSpecificNode(19, '', ['table:style-name'], ['DefaultColumnStyle']);
end;

procedure TQExport4ODT.ClearBufferedRow;
var
  i: Integer;
begin
  for i := 0 to Length(FBufferRow) - 1 do
    FBufferRow[i] := '';
end;

function TQExport4ODT.ColorToString(Color: TColor): string;

  procedure SwapNums(Num1, Num2: Integer; var SwapStr: String);
  var
    a: char;
  begin
    a := SwapStr[Num1];
    SwapStr[Num1] := SwapStr[Num2];
    SwapStr[Num2] := a;
  end;

begin
  //Get Standard TColor representation
  FmtStr(Result, '%.6x', [Color]);
  //Swap 2 bytes to get defaul RGB representation
  SwapNums(1, 5, Result);
  SwapNums(2, 6, Result);
  Result := '#' + Result;
end;

constructor TQExport4ODT.Create(AOwner: TComponent);
begin
  inherited;
  ODTFile := TBaseODFFile.Create;
  FODTOptions := TQExportODTOptions.Create(Self);
  FTableName := {$IFDEF WIN32}QExportLoadStr(QED_ODT_StandardTableName){$ENDIF}
     {$IFDEF LINUX}QED_ODT_StandardTableName{$ENDIF};
end;

destructor TQExport4ODT.Destroy;
begin
  ODTFile.Free;
  FODTOptions.Free;
  inherited;
end;

procedure TQExport4ODT.EndExport;
var
  i: Integer;
  temp: WideString;
begin
  if (AllowCaptions) then
    Inc(FRowCounter);
  ODTFile.WriteSpecificNode(1015, '', ['table:name'], [TableName]);
  for i := 0 to Footer.Count - 1 do
  begin
    temp := Footer[I];
    ODTFile.WriteSpecificNode(18, ReplaceSymbols(temp), ['text:style-name'], ['Footer']);
  end;
  ODTFile.CloseWriter;
  ODTFile.CloseStream;
  ODTFile.EndFormingContent;
  AddStyles;
  ODTFile.MergeContent('content.xml', ['first.xml', 'facefont.xml', 'auto.xml',
    'table.xml', 'last.xml' ]);
  ODTFile.MergeContent('styles.xml', ['firstSt.xml', 'facefontstyles.xml',
    'autoStyles.xml']);
  ODTFile.DeleteList(['first.xml', 'facefont.xml', 'auto.xml',
    'table.xml', 'last.xml', 'firstSt.xml', 'facefontstyles.xml',
    'autoStyles.xml']);
  ODTFile.Compress;
  inherited;
end;

procedure TQExport4ODT.Execute;
begin
  if TableName = '' then
    raise Exception.Create('Table name is not defined');
  DoExport;
  ShowResult;
end;


function TQExport4ODT.PointsToCms(Value: Integer): string;
  var
    TempString: string;
    Idx: Integer;
  begin
    if Value = 0 then
    begin
      Result := '0cm';
      Exit;
    end;
    TempString := FloatToStr(Value * 0.0352 / 2);
    Idx := QEPos(',', TempString);
    TempString[Idx] := '.';
    Result := Copy(TempString, 1, 5);
    Result := Result + 'cm';
end;

procedure TQExport4ODT.SetOptions(const Value: TQExportODTOptions);
begin
  FODTOptions.Assign(Value);
end;

procedure TQExport4ODT.WriteBufferedRow(TypeOfRow: Integer);
var
  i, Num: Integer;
begin
  Num := 0;
  //Default Caption row style w/o layer borders
  if TypeOfRow = 0 then
  begin
    if not AllowCaptions then Exit;
    ODTFile.WriteSpecificNode(30, '', [], []);
    ODTFile.WriteSpecificNode(16, '', [], []);
    for i := 0 to Length(FBufferRow) - 1 do
    begin
      if (i < Columns.Count - 1) then
        ODTFile.WriteSpecificNode(17, '', ['table:style-name',
          'office:value-type'], ['CaptionCellRow', 'string'])
      else
      begin
        if ODTOptions.Border.BorderStyle = bsODFSolid then
          ODTFile.WriteSpecificNode(17, '', ['table:style-name',
            'office:value-type'], ['EnderCaptionCellRow', 'string'])
        else
          ODTFile.WriteSpecificNode(17, '', ['table:style-name',
            'office:value-type'], ['CaptionCellRow', 'string']);
      end;
      ODTFile.WriteSpecificNode(18, FBufferRow[i], ['text:style-name'],
        ['CaptionRow']);
      ODTFile.WriteSpecificNode(1017, '', [], []);
    end;
    ODTFile.WriteSpecificNode(1016, '', [], []);
    ODTFile.WriteSpecificNode(1030, '', [], []);
  end;
  //Default Data style w/o layer borders
  if TypeOfRow = 1 then
  begin
    ODTFile.WriteSpecificNode(16, '', [], []);
    for i := 0 to Length(FBufferRow) - 1 do
    begin
      if (ODTOptions.StripStyle = sstNone) then
      begin
        if (i < Length(FBufferRow) - 1) then
            ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['DataCellStyle', 'string'])
        else
        begin
          if ODTOptions.Border.BorderStyle = bsODFSolid then
            ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['EnderDataCellStyle', 'string'])
          else
            ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['DataCellStyle', 'string']);
        end;
        ODTFile.WriteSpecificNode(18, FBufferRow[i], ['text:style-name'],
          ['DataStyle']);
      end
      else
      begin
        //Column-style
        if (ODTOptions.StripStyle = sstColumn) then
        begin
          Num := (i + 1) mod ODTOptions.StripStylesList.Count;
          if Num = 0 then
            Num := ODTOptions.StripStylesList.Count;
          if (i < Length(FBufferRow) - 1) then
             ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['StripCellStyle' + IntToStr(Num), 'string'])
          else
          begin
            if ODTOptions.Border.BorderStyle = bsODFSolid then
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['EnderStripCellStyle' + IntToStr(Num), 'string'])
            else
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['StripCellStyle' + IntToStr(Num), 'string']);
          end;
        end;
        //Row-style
        if (ODTOptions.StripStyle = sstRow) then
        begin
          Num := FRowCounter mod ODTOptions.StripStylesList.Count;
          if Num = 0 then
            Num := ODTOptions.StripStylesList.Count;
          if (i < Length(FBufferRow) - 1) then
            ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['StripCellStyle' + IntToStr(Num), 'string'])
          else
          begin
            if ODTOptions.Border.BorderStyle = bsODFSolid then
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['EnderStripCellStyle' + IntToStr(Num), 'string'])
           else
             ODTFile.WriteSpecificNode(17, '', ['table:style-name',
               'office:value-type'], ['StripCellStyle' + IntToStr(Num), 'string']);
          end;
        end;
        ODTFile.WriteSpecificNode(18, FBufferRow[i], ['text:style-name'],
          ['StripStyle' + IntToStr(Num)]);
      end;
      ODTFile.WriteSpecificNode(1017, '', [], []);
    end;
    ODTFile.WriteSpecificNode(1016, '', [], []);
  end;
  //Default Caption row style with layer borders
  if TypeOfRow = 2 then
  begin
    if not AllowCaptions then Exit;
    ODTFile.WriteSpecificNode(30, '', [], []);
    ODTFile.WriteSpecificNode(16, '', [], []);
    for i := 0 to Length(FBufferRow) - 1 do
    begin
      if (i < Columns.Count - 1) then
      begin
        if ODTOptions.Border.BorderStyle = bsODFSolid then
          ODTFile.WriteSpecificNode(17, '', ['table:style-name',
            'office:value-type'], ['LayerCaptionCellRow', 'string'])
        else
          ODTFile.WriteSpecificNode(17, '', ['table:style-name',
            'office:value-type'], ['CaptionCellRow', 'string']);
      end
      else
      begin
        if ODTOptions.Border.BorderStyle = bsODFSolid then
          ODTFile.WriteSpecificNode(17, '', ['table:style-name',
            'office:value-type'], ['LayerEnderCaptionCellRow', 'string'])
        else
          ODTFile.WriteSpecificNode(17, '', ['table:style-name',
            'office:value-type'], ['CaptionCellRow', 'string']);
      end;
      ODTFile.WriteSpecificNode(18, FBufferRow[i], ['text:style-name'],
        ['CaptionRow']);
      ODTFile.WriteSpecificNode(1017, '', [], []);
    end;
    ODTFile.WriteSpecificNode(1016, '', [], []);
    ODTFile.WriteSpecificNode(1030, '', [], []);
  end;
  //Default Data style with layer borders
  if TypeOfRow = 3 then
  begin
    ODTFile.WriteSpecificNode(16, '', [], []);
    for i := 0 to Length(FBufferRow) - 1 do
    begin
      if (ODTOptions.StripStyle = sstNone) then
      begin
        if (i < Length(FBufferRow) - 1) then
        begin
          if ODTOptions.Border.BorderStyle = bsODFSolid then
            ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['LayerDataCellStyle', 'string'])
          else
            ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['DataCellStyle', 'string']);
        end
        else
        begin
          if ODTOptions.Border.BorderStyle = bsODFSolid then
            ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['LayerEnderDataCellStyle', 'string'])
          else
            ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['DataCellStyle', 'string']);
        end;
        ODTFile.WriteSpecificNode(18, FBufferRow[i], ['text:style-name'],
          ['DataStyle']);
      end
      else
      begin
        //Column-style
        if (ODTOptions.StripStyle = sstColumn) then
        begin
          Num := (i + 1) mod ODTOptions.StripStylesList.Count;
          if Num = 0 then
            Num := ODTOptions.StripStylesList.Count;
          if (i < Length(FBufferRow) - 1) then
          begin
            if ODTOptions.Border.BorderStyle = bsODFSolid then
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['LayerStripCellStyle' + IntToStr(Num), 'string'])
            else
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['StripCellStyle' + IntToStr(Num), 'string']);
          end
          else
          begin
            if ODTOptions.Border.BorderStyle = bsODFSolid then
             ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['LayerEnderStripCellStyle' + IntToStr(Num), 'string'])
            else
             ODTFile.WriteSpecificNode(17, '', ['table:style-name',
              'office:value-type'], ['StripCellStyle' + IntToStr(Num), 'string']);
          end;
        end;
        //Row-style
        if (ODTOptions.StripStyle = sstRow) then
        begin
          Num := FRowCounter mod ODTOptions.StripStylesList.Count;
          if Num = 0 then
            Num := ODTOptions.StripStylesList.Count;
          if (i < Length(FBufferRow) - 1) then
          begin
            if ODTOptions.Border.BorderStyle = bsODFSolid then
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['LayerStripCellStyle' + IntToStr(Num), 'string'])
            else
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['StripCellStyle' + IntToStr(Num), 'string']);
          end
          else
          begin
            if ODTOptions.Border.BorderStyle = bsODFSolid then
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['LayerEnderStripCellStyle' + IntToStr(Num), 'string'])
            else
              ODTFile.WriteSpecificNode(17, '', ['table:style-name',
                'office:value-type'], ['StripCellStyle' + IntToStr(Num), 'string']);
          end;
        end;
        ODTFile.WriteSpecificNode(18, FBufferRow[i], ['text:style-name'],
          ['StripStyle' + IntToStr(Num)]);
      end;
      ODTFile.WriteSpecificNode(1017, '', [], []);
    end;
    ODTFile.WriteSpecificNode(1016, '', [], []);
  end;
end;

procedure TQExport4ODT.WriteCaptionRow;
var
  I: Integer;
  CurrValue: WideString;
begin
  for i := 0 to Columns.Count - 1 do
  begin
    CurrValue := GetColCaption(i);
    FBufferRow[i] := ReplaceSymbols(CurrValue);
  end;
end;

//Buffer conception - allocate current row in memory
//If WriteDataRow executed, it means, that another one row exists
//Then we can move buffered data into file and form another buffer
//If this method hasn't executed, then the last row formed inside
//After Export method
procedure TQExport4ODT.WriteDataRow;
var
  I: Integer;
  CurrValue: WideString;
begin
  //Here called the buffered row writing
  //null parameter is for Caption row - need to form correct styles
  //other parameters - for default
  if (FRowCounter = 0) then
    WriteBufferedRow(0)
  else
    WriteBufferedRow(1);
  ClearBufferedRow;
  for I := 0 to ExportRow.Count - 1 do
  begin
    Formats.ApplyParams;
    CurrValue := GetExportedValue(ExportRow[I]);
    Formats.RestoreSeparators;
    FBufferRow[I] := ReplaceSymbols(CurrValue);
  end;
  Inc(FRowCounter);
end;

{ TQExportODTOptions }

procedure TQExportODTOptions.Assign(Source: TPersistent);
begin
  if Source is TQExportODTOptions then begin
    HeaderStyle := (Source as TQExportODTOptions).HeaderStyle;
    FooterStyle := (Source as TQExportODTOptions).FooterStyle;
    CaptionRowStyle := (Source as TQExportODTOptions).CaptionRowStyle;
    DataStyle := (Source as TQExportODTOptions).DataStyle;
    Border.BorderStyle := (Source as TQExportODTOptions).Border.BorderStyle;
    Border.BorderWidth := (Source as TQExportODTOptions).Border.BorderWidth;
    Border.BorderColor := (Source as TQExportODTOptions).Border.BorderColor;
    StripStylesList := (Source as TQExportODTOptions).StripStylesList;
    StripStyle := (Source as TQExportODTOptions).StripStyle;
    Exit;
  end;
  inherited;
end;

function TQExportODTOptions.CheckFontInList(FontName: string): Boolean;
var
  I: Integer;
begin
  Result := false;
  for I := 0 to FontList.Count - 1 do
    if (FontName = FontList[I]) then
    begin
      Result := true;
      Exit;
    end;
end;

constructor TQExportODTOptions.Create(Holder: TPersistent);
begin
  inherited Create;
  FHolder := Holder;
  FHeaderStyle := TODTParagraphStyle.Create(nil);
  FFooterStyle := TODTParagraphStyle.Create(nil);
  FCaptionRowStyle := TODTCellParagraphStyle.Create(nil);
  FDataStyle := TODTCellParagraphStyle.Create(nil);
  FStripStyle := sstNone;
  FStripStylesList := TODTStylesList.Create(Self);
  FFontList := TStringList.Create;
  FBorder := TODFBorder.Create;
  FBorder.BorderStyle := bsODFSolid;
end;

destructor TQExportODTOptions.Destroy;
begin
  FHeaderStyle.Free;
  FFooterStyle.Free;
  FCaptionRowStyle.Free;
  FDataStyle.Free;
  FStripStylesList.Free;
  FFontList.Free;
  FBorder.Free;
  inherited;
end;

function TQExportODTOptions.GetOwner: TPersistent;
begin
  Result := FHolder;
end;


procedure TQExportODTOptions.SetCaptionStyle(const Value: TODTCellParagraphStyle);
begin
  FCaptionRowStyle.Assign(Value);
end;

procedure TQExportODTOptions.SetDataStyle(const Value: TODTCellParagraphStyle);
begin
  FDataStyle.Assign(Value);
end;

procedure TQExportODTOptions.SetFooterStyle(const Value: TODTParagraphStyle);
begin
  FFooterStyle.Assign(Value);
end;

procedure TQExportODTOptions.SetHeaderStyle(const Value: TODTParagraphStyle);
begin
  FHeaderStyle.Assign(Value);
end;

procedure TQExportODTOptions.SetStripStyles(const Value: TODTStylesList);
begin
  FStripStylesList.Assign(Value);
end;

end.